/* ----------------------------------------------------------------------
 *	Shadow Quest
 *	Copyright (C) 2009 Prageeth Silva <prageeth@thenewcoders.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *  ----------------------------------------------------------------------
 */
package panel;

import java.util.ArrayList;

import map.Item;
import map.Weather;
import map.World;

import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;

import sq.FontLoader;
import sq.ImageLoader;
import sq.RPG;
import sq.game.Quest;
import units.Player;

public final class Panel {

    public static final int INVENTORY_MAX_COLS = 5;
    public static final int INVENTORY_MAX_ROWS = 6;
    private static final double PANEL_TRANSITION_RATE = 0.35;
    private static int panelHeight = 0;
    private static final int panelMaxHeight = 70;
    private static final int panelGap = 0;
    private static int inventoryWidth = 0;
    private static final int inventoryMaxWidth = 142;
    private Image panel, inv;							// the status panel.
    private Image arrowDown, arrowUp;					// inventory scrolling arrows
    private boolean panelTransition, panelStatus;		// the panel transition, the status of the panel visibility
    private boolean inventoryTransition, inventoryStatus;		// the panel transition, the status of the panel visibility
    private boolean beginning = true;					// start of the game and is teleporting
    private boolean hidingPanel;
    private boolean oldInventory, oldMinimap;
    private int inventoryGroup, inventorySel;
    private Animation selOK, selNO;
    private PanelButton inventory, minimapButton;
    private Minimap minimap;
    private World world;
    private ArrayList<PanelButton> buttons;

    public Panel(World world)
            throws SlickException {

        this.setWorld(world);
        this.setMinimap(new Minimap(world));

        this.setPanelTransitionDone(true);
        this.setInventoryGroup(0);
        this.setInventorySel(0);
        this.setHidingPanel(false);

        this.setOldInventory(false);
        this.setOldMinimap(false);

        this.setInventoryTransitionDone(true);

        this.setInventoryButtons(new ArrayList<PanelButton>(12));

        String dirPath = "assets/res/panel/";
        Image hov = new Image(dirPath + "inv_hover.png");
        Image sel = new Image(dirPath + "inv_selected.png");
        PanelButton b = new PanelButton(hov, sel, 745, 14);
        this.setInventoryButton(b);

//    	hov = new Image(dirPath+"mm_hover.png");
//    	sel = new Image(dirPath+"mm_selected.png");
//    	b = new PanelButton(hov, sel, 745, 14);
//    	this.setMinimapButton(b);



////    	 TODO minimap
//    	hov = new Image(dirPath+"inv_hover.png");
//    	sel = new Image(dirPath+"inv_selected.png");
//    	b = new PanelButton(hov, sel, 745, 14);
//    	this.setMinimap(b);

        this.setPanelImage(new Image(dirPath + "panel.png"));
        this.setInventoryImage(new Image(dirPath + "inventory.png"));
        this.setArrowDown(new Image(dirPath + "arrow_down.png"));
        this.setArrowUp(new Image(dirPath + "arrow_up.png"));
        this.setSelOK(new Image(dirPath + "sel_ok.png"));
        this.setSelNO(new Image(dirPath + "sel_no.png"));

    }

    public void update(World world, Input input, int delta) {

        boolean panel_key = input.isKeyPressed(Input.KEY_SPACE);

        if (this.beginning) {
            this.setPanelTransitionDone(false);
            this.showPanel(delta);
            this.setInventoryTransitionDone(false);
            this.setInventoryStatus(false);
            // TODO
            //this.setMinimapTransitionDone(false);
            //this.hideMinimap(delta);
            this.beginning = false;
        }

        if (this.isHidingPanel()) {
            this.hidePanel(delta);
        }

        if (panel_key || !this.isPanelTransitionDone()) {
            this.setPanelTransitionDone(false);
            if (!this.isPanelShown()) {
                this.showPanel(delta);
            } else {
                this.hidePanel(delta);
            }
        }

        this.updateInventory(input, delta);
        this.getMinimap().update(input, delta);


    }

    /** Renders the player's status panel.
     * @param g The current Slick graphics context.
     * @throws SlickException 
     */
    public void render(Graphics g, Weather weather) {

        Quest q = this.getWorld().getQuest();
        g.drawString(Double.toString(q.getCurrentPlayer().getHP()), 200, 760);

        if (this.isPanelShown() || !this.isPanelTransitionDone()) {

            // Panel colours
            Color LABEL = new Color(0.9f, 0.9f, 0.4f);          // Gold

            // Variables for layout
            String text;                	// Text to display
            int text_x, text_y;         	// Coordinates to draw text
            int text_width, text_height;	// Text dimensions
            int bar_x, bar_y;           	// Coordinates to draw rectangles
            int bar_width, bar_height;  	// Size of rectangle to draw
            int xp_bar_height;
            int hp_bar_width;           	// Size of red (HP) rectangle
            int panel_top;					// The top of the panel
            float xp_percent;
            float health_percent;       	// Player's health, as a percentage

            // Reset font size
            g.setFont(FontLoader.getFontOriginal());

            // Get the top dimensions
            panel_top = RPG.getScreenHeight() - Panel.getPanelHeight() + Panel.getPanelGap();

            // Display the weather conditions
            weather.renderHour(g, 611, panel_top + 28);

            // Display bar backgrounds
            ImageLoader.getPanelBgXP().draw(50, panel_top);
            ImageLoader.getPanelBgHP().draw(160, panel_top);

            // Draw XP bar
            bar_x = 50;
            bar_y = panel_top + 7;
            bar_width = 33;
            bar_height = 55;
            xp_percent = (float) (q.getCurrentPlayer().getXP() / q.getCurrentPlayer().getMaxXP());
            xp_bar_height = (int) (bar_height * xp_percent);
            g.setColor(new Color(1f, 0f, 0.8f, 0.8f));
            g.fillRect(bar_x, bar_y + (bar_height - xp_bar_height), bar_width, xp_bar_height);

            // Draw HP bar and value
            bar_x = 166;
            bar_y = panel_top + 27;
            bar_width = 90;
            bar_height = 25;
            text = Integer.toString((int) q.getCurrentPlayer().getHP())
                    + "/" + Integer.toString((int) q.getCurrentPlayer().getMaxHP());
            text_width = g.getFont().getWidth(text);
            text_height = g.getFont().getHeight(text);
            text_x = bar_x + (bar_width - text_width) / 2;
            text_y = bar_y + (bar_height - text_height) / 2 + 1;
            health_percent = (float) (q.getCurrentPlayer().getHP() / q.getCurrentPlayer().getMaxHP());
            hp_bar_width = (int) (bar_width * health_percent);
            g.setColor(new Color(1 - health_percent, health_percent, 0.0f, 0.8f));
            g.fillRect(bar_x, bar_y, hp_bar_width, bar_height);
            g.setColor(Color.white);
            g.drawString(text, text_x, text_y);

            // Display the Player Character photo
            q.getCurrentPlayer().getPhoto().draw(4, panel_top + 4);

            // Panel background image
            this.getPanelImage().draw(0, RPG.getScreenHeight() - Panel.getPanelHeight());

            // Display the player's level
            bar_width = 64;
            text = Integer.toString(q.getCurrentPlayer().getLevel());
            text_width = g.getFont().getWidth(text);
            text_x = 88 + (bar_width - text_width) / 2;
            g.setColor(LABEL);
            g.drawString(text, text_x, text_y);

            // Display the player's damage
            bar_width = 47;
            text = Integer.toString(q.getCurrentPlayer().getDamage());
            text_width = g.getFont().getWidth(text);
            text_x = 295 + (bar_width - text_width) / 2;
            g.setColor(LABEL);
            g.drawString(text, text_x, text_y);

            // Display the player's range damage
            text = Integer.toString(q.getCurrentPlayer().getRangeDamage());
            text_width = g.getFont().getWidth(text);
            text_x = 371 + (bar_width - text_width) / 2;
            g.drawString(text, text_x, text_y);

            // Display the player's defence
            text = Integer.toString(q.getCurrentPlayer().getDefence());
            text_width = g.getFont().getWidth(text);
            text_x = 446 + (bar_width - text_width) / 2;
            g.drawString(text, text_x, text_y);

            // Display the player's cooldown
            bar_width = 86;
            text = Integer.toString(q.getCurrentPlayer().getMaxCoolDown());
            text_width = g.getFont().getWidth(text);
            text_x = 511 + (bar_width - text_width) / 2;
            g.drawString(text, text_x, text_y);

            // Display the inventory
            this.renderInventory(g);

            // Display the minimap
            this.getMinimap().render(g);

        }

    }

    /** Renders the player's status panel.
     * @param g The current Slick graphics context.
     * @throws SlickException 
     */
    protected void renderInventory(Graphics g) {

        Quest q = this.getWorld().getQuest();
        int inv_x, inv_y;           	// Coordinates to draw inventory item
        int panel_left, tot;			// The top of the panel
        int gap_w = 5;
        int gap_h = 4;

        if (this.isInventoryShown() || !this.isInventoryTransitionDone()) {

            // Get the top dimensions
            panel_left = RPG.getScreenWidth() - Panel.getInventoryWidth();

            // Panel background image
            this.getInventoryImage().draw(panel_left, 0);

            // Display the player's inventory
            inv_x = panel_left + 14 - gap_w;
            inv_y = 25 - gap_h;
            tot = Panel.INVENTORY_MAX_COLS * Panel.INVENTORY_MAX_ROWS;
            int start = this.getInventoryGroup() * tot;
            int end = start + tot;
            for (int i = start; i < end; i++) {
                if (i < q.getCurrentPlayer().getInventory().size()) {
                    Item item = q.getCurrentPlayer().getInventory().get(i);
                    if (item.isPickedUp()) {
                        int diff = i - start;
                        int x = (diff % Panel.INVENTORY_MAX_COLS) * Item.DEFAULT_WIDTH;
                        int y = (diff % Panel.INVENTORY_MAX_ROWS) * Item.DEFAULT_HEIGHT;
                        item.renderOnPanel(g, inv_x + x + gap_w, inv_y + y + gap_h);
                    }
                }
            }

            // Display the player's inventory usage
            int index = this.getInventoryGroup() * Panel.INVENTORY_MAX_COLS + this.getInventorySel();

            int invWidth = 57;
            if (q.getCurrentPlayer().getInventory().size() > 0
                    && index < q.getCurrentPlayer().getInventory().size()) {
                inv_x = 509 + this.getInventorySel() * invWidth;
                Item i = q.getCurrentPlayer().getInventory().get(index);
                if (i.isUsable()) {
                    this.getSelOK().draw(inv_x, inv_y);
                } else {
                    this.getSelNO().draw(inv_x, inv_y);
                }
            }

            // Display the inventory up / down buttons
            inv_y = panel_left - Panel.getPanelGap() + 4;
            inv_x = 746;
            if (q.getCurrentPlayer().getInventory().size() > Panel.INVENTORY_MAX_COLS) {
                int max = q.getCurrentPlayer().getInventory().size() / (Panel.INVENTORY_MAX_COLS + 1);
                if (this.getInventoryGroup() > 0) {
                    this.getArrowUp().draw(inv_x, inv_y);
                }
                if (this.getInventoryGroup() < max) {
                    inv_x += 23;
                    this.getArrowDown().draw(inv_x, inv_y);
                }
            }

        }

    }

    private Animation getSelOK() {
        return this.selOK;
    }

    private void setSelOK(Image image) {
        this.selOK = new Animation(new SpriteSheet(image, 60, 60), 200);
    }

    private Animation getSelNO() {
        return this.selNO;
    }

    private void setSelNO(Image image) {
        this.selNO = new Animation(new SpriteSheet(image, 60, 60), 200);
    }

    /** Get the panel image.
     * @return The panel Image.
     */
    private Image getPanelImage() {
        return this.panel;
    }

    /** Set the panel image.
     * @param image The panel image.
     */
    private void setPanelImage(Image image) {
        this.panel = image;
    }

    /** Get the panel image.
     * @return The panel Image.
     */
    private Image getInventoryImage() {
        return this.inv;
    }

    /** Set the panel image.
     * @param image The panel image.
     */
    private void setInventoryImage(Image inv) {
        this.inv = inv;
    }

    /** Get the panel's transition status.
     * @return inventory.
     */
    private boolean isPanelTransitionDone() {
        return this.panelTransition;
    }

    /** Set the panel's transition status.
     * @param panelTransition The transition status.
     */
    private void setPanelTransitionDone(boolean panelTransition) {
        this.panelTransition = panelTransition;
    }

    /** Get the panel's status.
     * @return The panel's status.
     */
    public boolean isPanelShown() {
        return this.panelStatus;
    }

    /** Set the panel's status.
     * @param panelStatus The panel's status.
     */
    private void setPanelStatus(boolean panelStatus) {
        this.panelStatus = panelStatus;
    }

    /** Get the panel's transition status.
     * @return inventory.
     */
    private boolean isInventoryTransitionDone() {
        return this.inventoryTransition;
    }

    /** Set the panel's transition status.
     * @param panelTransition The transition status.
     */
    private void setInventoryTransitionDone(boolean inventoryTransition) {
        this.inventoryTransition = inventoryTransition;
    }

    /** Get the panel's status.
     * @return The panel's status.
     */
    public boolean isInventoryShown() {
        return this.inventoryStatus;
    }

    /** Set the panel's status.
     * @param panelStatus The panel's status.
     */
    private void setInventoryStatus(boolean inventoryStatus) {
        this.inventoryStatus = inventoryStatus;
    }

    private Image getArrowDown() {
        return this.arrowDown;
    }

    private void setArrowDown(Image arrowDown) {
        this.arrowDown = arrowDown;
    }

    private Image getArrowUp() {
        return this.arrowUp;
    }

    private void setArrowUp(Image arrowUp) {
        this.arrowUp = arrowUp;
    }

    public int getInventoryGroup() {
        return this.inventoryGroup;
    }

    private void setInventoryGroup(int inventoryGroup) {
        this.inventoryGroup = inventoryGroup;
    }

    public void goUpGroup() {
        int tmp = this.getInventoryGroup() - 1;
        this.setInventoryGroup(tmp < 0 ? 0 : tmp);
    }

    public void goDownGroup() {
        int tmp = this.getInventoryGroup() + 1;
        int max = this.getWorld().getQuest().getCurrentPlayer().getInventory().size() / (Panel.INVENTORY_MAX_COLS + 1);
        this.setInventoryGroup(tmp > max ? max : tmp);
    }

    private int getInventorySel() {
        return this.inventorySel;
    }

    private void setInventorySel(int sel) {
        Player p = this.getWorld().getQuest().getCurrentPlayer();
        int max = (p != null) ? p.getInventory().size() : 0;
        int last = max - this.getInventoryGroup() * Panel.INVENTORY_MAX_COLS - 1;
        last = Math.min(last, Panel.INVENTORY_MAX_COLS - 1);
        if (sel >= 0 && sel <= max) {
            this.inventorySel = (sel >= 0 && sel <= last) ? sel : 0;
        } else {
            this.inventorySel = last;
        }
    }

    private void updateInventory(Input input, int delta) {

        Quest q = this.getWorld().getQuest();

        boolean key_0 = input.isKeyPressed(Input.KEY_0);
        boolean key_1 = input.isKeyPressed(Input.KEY_1);
        boolean key_2 = input.isKeyPressed(Input.KEY_2);
        boolean key_3 = input.isKeyPressed(Input.KEY_3);
        boolean key_4 = input.isKeyPressed(Input.KEY_4);
        boolean key_5 = input.isKeyPressed(Input.KEY_5);
        boolean key_use = input.isKeyPressed(Input.KEY_U);
        boolean key_back = input.isKeyPressed(Input.KEY_7);
        boolean key_forward = input.isKeyPressed(Input.KEY_8);
        boolean inv_up = input.isKeyPressed(Input.KEY_9);
        boolean inv_down = input.isKeyPressed(Input.KEY_0);


        if (key_1) {
            this.setInventorySel(0);
        } else if (key_2) {
            this.setInventorySel(1);
        } else if (key_3) {
            this.setInventorySel(2);
        } else if (key_4) {
            this.setInventorySel(3);
        } else if (key_5) {
            this.setInventorySel(4);
        } else {
            //make sure the items still exist
            this.setInventorySel(this.getInventorySel());
        }

        if (key_back) {
            this.setInventorySel(this.getInventorySel() - 1);
        }
        if (key_forward) {
            this.setInventorySel(this.getInventorySel() + 1);
        }

        if (key_use) {

            int i = this.getInventoryGroup() * Panel.INVENTORY_MAX_COLS + this.getInventorySel();
            if (q.getCurrentPlayer().getInventory().size() > 0
                    && i < q.getCurrentPlayer().getInventory().size()) {
                Item item = q.getCurrentPlayer().getInventory().get(i);
                if (item.isUsable()) {
                    item.use(q.getCurrentPlayer());
                    if (item.getItemType() == Item.ItemType.BOW) {
                        q.getCurrentPlayer().setUsingUprageStatus(true);
                    } else if (item.getItemType() == Item.ItemType.EXPLOSIVE) {
                        q.getCurrentPlayer().dropInventoryItem(i, q.getCurrentPlayer().getX(), q.getCurrentPlayer().getY(), false);
                    } else if (item.getItemType() == Item.ItemType.POTION) {
                        q.getCurrentPlayer().dropInventoryItem(i, q.getCurrentPlayer().getX(), q.getCurrentPlayer().getY(), false);
                    }
                }
            }

        }

        if (inv_up) {
            this.goUpGroup();
        }
        if (inv_down) {
            this.goDownGroup();
        }

        // TODO
        if ((key_0 && this.isPanelShown() && this.isPanelTransitionDone())
                || !this.isInventoryTransitionDone()) {
            this.setInventoryTransitionDone(false);
            if (!this.isInventoryShown()) {
                this.showInventory(delta);
            } else {
                this.hideInventory(delta);
            }
        }

    }

    /** shows the status panel.
     * @param delta Time passed since last frame (milliseconds).
     */
    public void showPanel(int delta) {
        if (!this.isPanelTransitionDone()) {

            Panel.setPanelHeight(Panel.getPanelHeight() + (int) Math.floor(PANEL_TRANSITION_RATE * delta));

            if (Panel.getPanelHeight() >= Panel.getPanelMaxHeight()) {

                Panel.setPanelHeight(Panel.getPanelMaxHeight());
                this.setPanelStatus(true);
                this.setPanelTransitionDone(true);

                if (this.isOldInventory()) {
                    this.setOldInventory(false);
                    this.setInventoryTransitionDone(false);
                }

                if (this.isOldMinimap()) {
                    this.setOldMinimap(false);
                    this.getMinimap().show();
                }

            }

        }
    }

    /** hides the status panel.
     * @param delta Time passed since last frame (milliseconds).
     */
    public void hidePanel(int delta) {

        boolean bInventory = (this.isInventoryShown() || !this.isInventoryTransitionDone());
        boolean bMinimap = (this.getMinimap().isShown() || !this.getMinimap().isTransitionDone());

        if (bInventory || bMinimap) {
            if (bInventory) {
                this.setInventoryTransitionDone(false);
                this.setHidingPanel(true);
                this.setOldInventory(true);
            }
            if (bMinimap) {
                this.getMinimap().hide();
                this.setOldMinimap(true);
            }
        } else if (!this.isPanelTransitionDone()) {
            Panel.setPanelHeight(Panel.getPanelHeight() - (int) Math.floor(PANEL_TRANSITION_RATE * delta));
            if (Panel.getPanelHeight() <= 0) {
                Panel.setPanelHeight(0);
                this.setPanelStatus(false);
                this.setPanelTransitionDone(true);
                this.setHidingPanel(false);
            }
        }
    }

    /** shows the status panel.
     * @param delta Time passed since last frame (milliseconds).
     */
    protected void showInventory(int delta) {
        if (!this.isInventoryTransitionDone()) {
            Panel.setInventoryWidth(Panel.getInventoryWidth() + (int) Math.floor(PANEL_TRANSITION_RATE * delta));
            if (Panel.getInventoryWidth() >= Panel.getInventoryMaxWidth()) {
                Panel.setInventoryWidth(Panel.getInventoryMaxWidth());
                this.setInventoryStatus(true);
                this.setInventoryTransitionDone(true);
            }
        }
    }

    /** hides the status panel.
     * @param delta Time passed since last frame (milliseconds).
     */
    protected void hideInventory(int delta) {
        if (!this.isInventoryTransitionDone()) {
            Panel.setInventoryWidth(Panel.getInventoryWidth() - (int) Math.floor(PANEL_TRANSITION_RATE * delta));
            if (Panel.getInventoryWidth() <= 0) {
                Panel.setInventoryWidth(0);
                this.setInventoryStatus(false);
                this.setInventoryTransitionDone(true);
            }
        }
    }

    protected boolean isHidingPanel() {
        return this.hidingPanel;
    }

    protected void setHidingPanel(boolean hidingPanel) {
        this.hidingPanel = hidingPanel;
    }

    public PanelButton getInventoryButton() {
        return this.inventory;
    }

    private void setInventoryButton(PanelButton inventory) {
        this.inventory = inventory;
    }

    public PanelButton getMinimapButton() {
        return this.minimapButton;
    }

    private void setMinimapButton(PanelButton minimapButton) {
        this.minimapButton = minimapButton;
    }

    public Minimap getMinimap() {
        return this.minimap;
    }

    private void setMinimap(Minimap minimap) {
        this.minimap = minimap;
    }

    private ArrayList<PanelButton> getInventoryButtons() {
        return buttons;
    }

    protected void setInventoryButtons(ArrayList<PanelButton> buttons) {
        this.buttons = buttons;
    }

    protected void addButton(PanelButton button) {
        this.getInventoryButtons().add(button);
    }

    public boolean isOldInventory() {
        return this.oldInventory;
    }

    public void setOldInventory(boolean oldInventory) {
        this.oldInventory = oldInventory;
    }

    public boolean isOldMinimap() {
        return this.oldMinimap;
    }

    public void setOldMinimap(boolean oldMinimap) {
        this.oldMinimap = oldMinimap;
    }

    public static void setPanelHeight(int panelheight) {
        Panel.panelHeight = panelheight;
    }

    public static int getPanelHeight() {
        return Panel.panelHeight;
    }

    public static int getPanelMaxHeight() {
        return Panel.panelMaxHeight;
    }

    public static int getPanelGap() {
        return Panel.panelGap;
    }

    public static int getInventoryWidth() {
        return Panel.inventoryWidth;
    }

    public static void setInventoryWidth(int inventoryWidth) {
        Panel.inventoryWidth = inventoryWidth;
    }

    public static int getInventoryMaxWidth() {
        return Panel.inventoryMaxWidth;
    }

    /**
     * @return the world
     */
    private World getWorld() {
        return this.world;
    }

    /**
     * @param world the world to set
     */
    private void setWorld(World world) {
        this.world = world;
    }
    
}
